#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "Lily_help.h"

uint32_t lily_built_in_millis(void);
void lily_delay(uint32_t ms);
void *lily_out_queue(void *arg, char *msg, int32_t len);

void (*_li_Delay)(uint32_t ms) = lily_delay;
uint32_t (*_li_millis)() = lily_built_in_millis;

const char *__li_error_msg = NULL;
const char *__li_error_msg2 = NULL;
const char *__li_error_loc = NULL;
int32_t r_count = 0;
float __Nanf = 0;
const char *_ver = _VersionMsg ":" __DATE__ "/" __TIME__;

void BETA lily_in(char c)
{
	static int _s = 0;
	static char _qc = 0;
	r_count++;
	if (!c || c == '\r')
		return;
	static int _ic = 0, _iqc = 0;
	if (c != '\b')
		_ic++;
	switch (_s)
	{
	case 0:
		if (c == '\\')
		{
			_s = 1;
			_ic--;
			return;
		}
		if (c == '\'' || c == '\"')
		{
			_s = 2;
			_qc = c;
			break;
		}
		if (c == '\b')
		{
			if (_ic)
			{
				_ic--;
				ri--;
				if (ri == hi)
				{
					_ic = 0;
				}
			}
			return;
		}
		if (c == '\n')
		{
			rx[ri++] = '\0';
			if (!_cmd_queue_in_waiting && !_lily_is_busy)
			{
				tp_addTask(_get_cmd_from_rx);
			}
			_cmd_queue_in_waiting++;
			_ic = 0;
			return;
		}
		break;
	case 1:
		_s = 0;
		if (c == '\n' || c == '\b')
		{
			_ic--;
			return;
		}
		break;
	case 2:
		if (c == '\b')
		{
			if (_iqc)
			{
				_iqc--;
				_ic--;
				ri--;
			}
			else
			{
				_s = 0;
			}
			return;
		}
		else
		{
			_iqc++;
		}

		if (c == _qc)
		{
			_s = 0;
			_qc = 0;
			break;
		}
		if (c == '\\')
		{
			_s = 3;
			break;
		}
		break;
	case 3:
		if (c == '\b')
		{
			_s = 2;
			_iqc--;
			_ic--;
			ri--;
			return;
		}
		_s = 2;
		break;
	default:
		_s = 0;
		break;
	}
	rx[ri++] = c;
	if (ri == hi)
	{
		rx[ri++] = c;
		_s = 0;
	}
}

void echo_version_number()
{
	char tx[TX_len];
	sprintf(tx, "%s:%d.%d.%d\nbuilt on:%s\n",_VersionMsg, _VersionNum >> 16, (_VersionNum >> 8) & 0xff, _VersionNum & 0xff, __DATE__);
	lily_out(tx);
}
float time_cmd()
{
	char tx[TX_len];
	uint32_t t = _li_millis();
	sprintf(tx, "%u\n", t);
	lily_out(tx);
	return (float)t;
}

void echo_error()
{
	char tx[TX_len];
	if (__li_error_msg2 == NULL)
		return;
	if (__li_error_loc && lily_ui.rtc < 0)
	{
		#if defined(__WIN32__)
		lily_out(strchr(__li_error_loc, '\\') ? strrchr(__li_error_loc, '\\') + 1 : __li_error_loc);
		#else
		lily_out(strrchr(__li_error_loc, '/') ? strrchr(__li_error_loc, '/') + 1 : __li_error_loc);
		#endif
		sprintf(tx, "(%d):", -lily_ui.rtc);
		lily_out(tx);
	}
	lily_out((char *)__li_error_msg2);
	lily_out("\n");
}

// the pipe entry stream for lily
void *lily_in_for_pipe_entry(void *arg, char *data, int32_t len)
{
	while (len > 0)
	{
		lily_in(*data);
		len--;
		data++;
	}
	return NULL;
}

void lily_out(const char *msg)
{
	if (!msg)
		return;
	pipe_release_data(lily_ui.lily_std_pipe, (char *)msg, strlen(msg));
}

void lily_outf(const char *fmt, ...)
{
	if (!fmt)
		return;
	char buf[1024];
	va_list args;
	va_start(args, fmt);
	vsprintf(buf, fmt, args);
	va_end(args);
	lily_out(buf);
}
int lily_redirect_output(Pipe newLine)
{
	if (!newLine)
	{
		pipe_disconnect(lily_ui.lily_std_pipe);
		return 0;
	}
	pipe_connect_to(lily_ui.lily_std_pipe, newLine);
	return 0;
}
Pipe lily_get_current_pipe()
{
	return lily_ui.lily_std_pipe->next;
}

// set up lily_in and lily_out
// given a outer stream
// and return a in-stream which stands with Lily_std_in
// to use the return value, create a Pipe source for your keyboard or other input devices
//   and connect you pipe to Lily_std_in
Pipe lily_pipe_io_setup(Pipe pipe_to_std_out)
{
	lily_ui.lily_std_pipe = li_new_output_device("lily", NULL, lily_in_for_pipe_entry);
	if (!lily_ui.lily_std_pipe)
		return NULL;
	pipe_connect_to(lily_ui.lily_std_pipe, pipe_to_std_out);
	void *redirection_out_append_to_var(void *arg, const char *s, int32_t len);
	pipe_out_to_str_var = li_new_output_device("_to_var", NULL, (PipeLine)redirection_out_append_to_var);
	return lily_ui.lily_std_pipe;
}

void lily_init()
{
	__Nanf = __Nanf / __Nanf; // nan value used by lily
	lily_ui.cmds = new_list(sizeof(Cmd_t), 48);
	if (!lily_ui.cmds)
	{
		lily_out("lily cmds init failed!");
		return;
	}
	lily_ui.vars = new_list(sizeof(Var_t), 16);
	if (!lily_ui.vars)
	{
		lily_out("lily vars init failed!");
		return;
	}
	lily_ui.funs = new_list(sizeof(Fun_t), 4);
	if (!lily_ui.funs)
	{
		lily_out("lily funs init failed!");
		return;
	}

#define new_cmd(name, callback)               \
	{                                         \
		int callback(int argn, char *argv[]); \
		public_a_cmd_link(#name, callback);   \
	}
#define new_cmd2(name, callback, help_doc)                \
	{                                                     \
		int callback(int argn, char *argv[]);             \
		public_a_cmd_link_doc(#name, callback, help_doc); \
	}
#define new_cmd3(name, callback, help_doc_var)                \
	{                                                         \
		int callback(int argn, char *argv[]);                 \
		extern const char *help_doc_var;                      \
		public_a_cmd_link_doc(#name, callback, help_doc_var); \
	}
#define new_fun(name, callback, argn)               \
	{                                               \
		public_a_fun_link_n(#name, callback, argn); \
	}
#define new_fun_int(name, callback, argn)             \
	{                                                 \
		public_a_fun_link_int(#name, callback, argn); \
	}
#define new_var(name, ref, type)                       \
	{                                                  \
		extern type ref;                               \
		public_a_var_ref(#name, &ref, VarType_##type); \
	}
#define load(mod)          \
	{                      \
		void mod##_init(); \
		mod##_init();      \
	}
#define event(e)          \
	{                     \
		li_new_event(#e); \
	}
	new_fun(time, time_cmd, 0);
	new_fun(er, echo_error, 0);
	new_fun(version, echo_version_number, 0);

#include "Lily_modules.h"
#undef new_cmd
#undef new_cmd2
#undef new_fun
#undef new_fun_int
#undef new_var
#undef load

	
	public_a_var_ref("_ver", (void *)_ver, 's');
	public_a_var_ref("rcount", &r_count, 'd');
	public_a_var_ref("rtc", &(lily_ui.rtc), 'd');
}

void lily_delay(uint32_t ms)
{
	uint32_t i, j;
	for (i = 0; i < ms; i++)
		for (j = 0; j < 1000; j++)
			;
}
void *lily_out_queue(void *arg, char *msg, int32_t len)
{
	return NULL;
}
uint32_t lily_built_in_millis()
{
	return 0;
}
